package com.itheima.d9_tcp6;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/* * 扩展：
 * 每次请求都开一个新线程好不好：如果系统访问量不大，那还行。但是存在高并发时，容易宕机
 * 使用线程池进行优化：线程池可以控制线程的数量以及任务的数量
 * 假设一个线程池，包含两个核心线程，任务队列数量为八。然后每次来一个通信管道时，就把这个通信管道包装成一个runnable的任务对象
 * 这个任务对象里面的任务就是为了这个通信管道去响应一个网页，然后把这些任务对象都扔到线程池的任务队列中
 * 并且用线程池之后，也固定了服务端的线程数量和任务的数量，即便是高并发的时候，也不会引起系统瘫痪和死机的风险
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象，同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8080);

        /*
         创建出一个线程池，负责处理通信管道的任务:
        每个线程都是往客户端管道响应一个网页回去，属于io密集型，那么应该用当前计算机的这个线程数量*2，也就是16*2作为核心线程数量，
        最大线程数量也用32，相当于固定的线程数量就是32，我们没有设置临时线程，所以设置为0，单位也是零秒。然后是配置任务队列，
        缓存我们的管道任务，设置为缓存8个客户的请求。然后是创建线程工厂来帮忙创建这些核心线程，我们用executors工具类，
        调用该工具类的defaultThreadFactory方法，得到一个默认的线程工厂。最后拒绝策略，这里用AbortPolicy，
        代表新任务直接拒绝掉，抛出异常
        */
        ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2,
                0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(8) ,
                Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

        while (true) {
            // 2、使用serverSocket对象，调用一个accept方法，等待客户端的连接请求
            //在这个地方，一旦接收到了一个客户端的通信管道之后，就应该把这个通信管道封装成一个任务对象，交给线程池来处理
            Socket socket = serverSocket.accept();

            // 3、把这个客户端对应的socket通信管道，交给一个独立的线程负责处理。
/*
            那怎么样把这个socket通信管道封装成一个任务对象呢:
                只需要把ServerReaderThread线程类改为一个任务类就可以了，怎么改呢，其实这个线程类本身就是一个任务类，
                也是实现了Runnable接口，但是直接把这个线程类当做任务类来用，怕同学们看到后会晕乎乎，所以改造一下，让他直接实现Runnable接口

            创建了ServerReaderRunnable任务类，然后创建一个任务对象new ServerReaderRunnable()，
            然后把当前这个客户端通信管道，交给这个任务对象：new ServerReaderRunnable(socket)
            然后再把这个任务对象交给线程池pool，并且调用execute方法：pool.execute(new ServerReaderRunnable(socket));

            然后线程池就会用固定的线程数量来处理这个任务对象，处理这个任务对象就是执行ServerReaderRunnable里面的run方法

            ExecutorService的常用方法: void execute(Runnable command)  执行 Runnable 任务
                   */

            pool.execute(new ServerReaderRunnable(socket));
        }
    }
}













